﻿// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

namespace TYG.GateWay
{
    using DotNetty.Buffers;
    using DotNetty.Codecs;
    using DotNetty.Transport.Channels;
    using DotNetty.Transport.Channels.Groups;
    using Google.Protobuf;
    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Linq;
    using System.Net;
    using System.Threading.Tasks;
    using TYG.Proto;

    public class SecureChatServerHandler : SimpleChannelInboundHandler<object>
    {
        static volatile IChannelGroup group;
        static List<Models.Channel> LoginInfo = new List<Models.Channel>();
        static Dictionary<string, SendFrameRequest> UserRunData = new Dictionary<string, SendFrameRequest>();
        /// <summary>
        /// 信道激活
        /// </summary>
        /// <param name="contex"></param>
        public override void ChannelActive(IChannelHandlerContext contex)
        {
            IChannelGroup g = group;
            if (g == null)
            {
                lock (this)
                {
                    if (group == null)
                    {
                        g = group = new DefaultChannelGroup(contex.Executor);
                    }
                }
            }
            LoginInfo.Add(new Models.Channel { DnsName = Dns.GetHostName(), LongText = contex.Channel.Id.AsLongText(), ShortText = contex.Channel.Id.AsShortText(), ContexName = contex.Name, OnLine = Models.OnLine.On });
            //RedisRepository.RedisInstance.Instance.SingleSave(new Models.Channel { DnsName= Dns.GetHostName(), LongText= contex.Channel.Id.AsLongText(), ShortText= contex.Channel.Id.AsShortText(), ContexName= contex.Name, OnLine=  Models.OnLine.On });
            Console.WriteLine(string.Format("Welcome to {0} secure chat server!\n", Dns.GetHostName()));
            //  contex.WriteAndFlushAsync(string.Format("Welcome to {0} secure chat server!\n", Dns.GetHostName()));
            g.Add(contex.Channel);
        }

        class EveryOneBut : IChannelMatcher
        {
            readonly IChannelId id;

            public EveryOneBut(IChannelId id)
            {
                this.id = id;
            }

            public bool Matches(IChannel channel) => channel.Id != this.id;
        }


        /// <summary>
        /// 通道读取
        /// </summary>
        /// <param name="ctx"></param>
        /// <param name="msg"></param>
        protected override void ChannelRead0(IChannelHandlerContext ctx, object msg)
        {
            IByteBuffer buffer = (IByteBuffer)msg;
            if (Convert(buffer, TankModel.Parser)!=null)
            {
                var data = (TankModel)Convert(buffer, TankModel.Parser);
                group.WriteAndFlushAsync(data, new EveryOneBut(ctx.Channel.Id));
                ctx.WriteAndFlushAsync(data);

            }
            else if (Convert(buffer, SendFrameRequest.Parser) != null)
            {
                var data = (SendFrameRequest)Convert(buffer, SendFrameRequest.Parser);
                data.Playerid = LoginInfo.FirstOrDefault(a => a.ShortText == ctx.Channel.Id.AsShortText()).ShortText;
                group.WriteAndFlushAsync(data, new EveryOneBut(ctx.Channel.Id));
                ctx.WriteAndFlushAsync(data);
            }
            else if (Convert(buffer, HostReponse.Parser) != null)
            {
                var data = (SendFrameRequest)Convert(buffer, SendFrameRequest.Parser);
                var result = LoginInfo.Select(a => new HostReponse
                {
                    ContexName = a.ContexName,
                    ShortText = a.ShortText,
                    DnsName = a.DnsName,
                    LongText = a.LongText,
                    OnLine = (a.OnLine == Models.OnLine.On) ? HostReponse.Types.OnLineStatus.On : HostReponse.Types.OnLineStatus.Off
                }).ToList();
                group.WriteAndFlushAsync(result, new EveryOneBut(ctx.Channel.Id));
                ctx.WriteAndFlushAsync(result);
            }
            
            //var player=  RedisRepository.RedisInstance.Instance.SingleGet<Models.Channel>();

            //RedisRepository.RedisInstance.Instance.SingleSave(data);
            //string broadcast = string.Format("[{0}] {1}\n", ctx.Channel.RemoteAddress, "发送了消息");
            //string response = string.Format("[you] {0}\n","你发送了消息");
            Console.WriteLine("用户已登录");
            //group.WriteAndFlushAsync(data, new EveryOneBut(ctx.Channel.Id));
            //ctx.WriteAndFlushAsync(data);
        }

        /// <summary>
        /// 异常退出走这里
        /// </summary>
        /// <param name="context"></param>
        public override void HandlerRemoved(IChannelHandlerContext contex)
        {
            UserRunData.Remove(contex.Channel.Id.AsShortText());
            // RedisRepository.RedisInstance.Instance.SingleSave(new Models.Channel { DnsName = Dns.GetHostName(), LongText = contex.Channel.Id.AsLongText(), ShortText = contex.Channel.Id.AsShortText(), ContexName = contex.Name, OnLine = Models.OnLine.Off });
            base.HandlerRemoved(contex);
        }


        /// <summary>
        /// 通道读取完成
        /// </summary>
        /// <param name="ctx"></param>
        public override void ChannelReadComplete(IChannelHandlerContext ctx) => ctx.Flush();



        /// <summary>
        /// 异常检测
        /// </summary>
        /// <param name="ctx"></param>
        /// <param name="e"></param>
        public override void ExceptionCaught(IChannelHandlerContext ctx, Exception e)
        {

            Console.WriteLine("{0}", e.StackTrace);
            // group.CloseAsync();
            // ctx.CloseAsync();//关闭通道
        }
        public override Task CloseAsync(IChannelHandlerContext context)
        {
            Console.WriteLine("通道已经关闭" + context.Channel.RemoteAddress + context.Channel.Id);
            return base.CloseAsync(context);
        }
        /// <summary>
        /// 是否共享
        /// </summary>
        public override bool IsSharable => true;

        private IMessage Convert(IByteBuffer message, MessageParser messageParser) {
            int length = message.ReadableBytes;
            if (length <= 0)
            {
                return null;
            }

            Stream inputStream = null;
            try
            {
                CodedInputStream codedInputStream;
                if (message.IoBufferCount == 1)
                {
                    ArraySegment<byte> bytes = message.GetIoBuffer(message.ReaderIndex, length);
                    codedInputStream = new CodedInputStream(bytes.Array, bytes.Offset, length);
                }
                else
                {
                    inputStream = new ReadOnlyByteBufferStream(message, false);
                    codedInputStream = new CodedInputStream(inputStream);
                }

                //
                // Note that we do not dispose the input stream because there is no input stream attached. 
                // Ideally, it should be disposed. BUT if it is disposed, a null reference exception is 
                // thrown because CodedInputStream flag leaveOpen is set to false for direct byte array reads,
                // when it is disposed the input stream is null.
                // 
                // In this case it is ok because the CodedInputStream does not own the byte data.
                //
                IMessage decoded = messageParser.ParseFrom(codedInputStream);
                return decoded;
            }
            catch (Exception exception)
            {
                throw new CodecException(exception);
            }
            finally
            {
                inputStream?.Dispose();
            }
          
        }
    }
}